/*
 * @Author: xiaosihan
 * @Date: 2021-05-21 23:25:20
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-10-22 22:49:59
 */

import _ from "lodash";
import React from "react";
import * as THREE from "three";
import { Object3D } from "three";
import THREEInstancedMesh from "../InstancedMesh/THREEInstancedMesh";
import Line2 from "../Line2/Line2";
import Object3DComponent, { Object3DComponentProps } from "../Object3D/Object3DComponent";
import { Vector3 } from "../Object3D/type";
import xrt_util from "../xrt_util/xrt_util";


export interface RaycasterProps extends Object3DComponentProps {
    direction?: Vector3,// 射线方向
}

interface State {
    intersectState: boolean; // 拾取状态
}

export default class Raycaster extends Object3DComponent<RaycasterProps, State> {
    constructor(props: RaycasterProps) {
        super(props);
    }

    static defaultProps = {
        duration: 0
    }

    raycaster = new THREE.Raycaster();

    state: State = {
        intersectState: false
    }

    static quaternion = new THREE.Quaternion;

    static worldQuaternion = new THREE.Quaternion;

    static worldNormal = new THREE.Vector3;

    componentDidMount() {
        this.raycaster.far = this.getDirection().length();
    }

    componentDidUpdate() {
        this.raycaster.far = this.getDirection().length();
    }

    componentBeforeRender() {

        const { intersectState } = this.state;
        const { dev } = this.context;

        // 相交计算
        let intersects = this.getIntersetResult();

        if (dev) {
            if (!_.isEmpty(intersects) && !intersectState) {
                this.setState({ intersectState: true });
            } else if (_.isEmpty(intersects) && intersectState) {
                this.setState({ intersectState: false });
            }
        }

        return true;
    }

    // 获取射线的方向
    getDirection() {
        let { direction = { z: 1 } } = this.props;
        let newDirection = xrt_util.parseVector3(direction);
        return newDirection;
    }

    // 获取需要做相交计算的对象
    getIntersectObject3ds() {
        const { collideCubeInstanceMesh, collideCylindricalInstanceMesh, collidePlaneInstanceMesh } = this.context;
        return [collideCubeInstanceMesh, collideCylindricalInstanceMesh, collidePlaneInstanceMesh];
    }

    // 计算相交面的世界法向量
    _computerWorldNormal(intersect: THREE.Intersection<Object3D>) {

        if (intersect.face) {
            Raycaster.worldNormal.copy(intersect.face.normal); // 取出相交点的法线向量
        }

        if (_.get(intersect.object, ["isTHREEInstancedMesh"]) && !_.isUndefined(intersect.instanceId)) { // 如果是实例模型的话还需要 左乘一次实例矩阵

            (intersect.object as THREEInstancedMesh).instanceChrildren[intersect.instanceId].getWorldQuaternion(Raycaster.worldQuaternion); // 世界旋转由实例对象的子元素来设置

        } else {

            intersect.object.getWorldQuaternion(Raycaster.worldQuaternion); // 由对象的世界矩阵来设置世界旋转角度

        }

        Raycaster.worldNormal.applyQuaternion(Raycaster.worldQuaternion); // 计算当前相交的点的世界向量

        Raycaster.worldNormal.setLength(1); // 设置长度

        if (intersect.face) {
            //@ts-ignore
            intersect.face.worldNormal = Raycaster.worldNormal.clone();
        }

        return intersect;
    };

    // 获取相交计算的结果
    getIntersetResult(offset?: Vector3): THREE.Intersection<Object3D>[] {

        let intersctObject3ds = this.getIntersectObject3ds();

        this.object3d.updateMatrixWorld();

        this.object3d.getWorldPosition(this.raycaster.ray.origin);

        this.raycaster.ray.origin.add(xrt_util.parseVector3(offset));

        // 设置方向
        this.raycaster.ray.direction.copy(this.getDirection()).clampLength(0, 1);
        this.object3d.getWorldQuaternion(Raycaster.quaternion);
        this.raycaster.ray.direction.applyQuaternion(Raycaster.quaternion);

        // 相交计算
        let intersects = this.raycaster.intersectObjects(intersctObject3ds, false);

        intersects.map(intersect => {
            this._computerWorldNormal(intersect);
        });

        return intersects;
    }

    render() {

        let directionArray = Object.values(this.getDirection()) as [number, number, number];
        const { intersectState } = this.state;
        const { dev } = this.context;

        if (!dev) {
            return null;
        }

        return (
            <Line2
                duration={0}
                color={["#fff", intersectState ? "#f00" : "#2993cc"]}
                points={[[0, 0, 0], directionArray]}
                linewidth={2}
            />
        );
    }
}